package network_programming.np1_socket_and_serversocket;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;

/**
 * 设置Socket的选项
 *
 * TCP_NODELAY      表示立即发送数据，默认值为false
 * SO_RESUSEADDR    表示是否允许重用Socket所绑定的本地地址，默认值为false
 * SO_TIMEOUT       表示接收数据时的等待超时时间
 * SO_LINGER        表示当执行Socket的close()方法时，是否立即关闭底层的Socket
 * SO_SNDBUF        表示发送数据的缓冲区大小
 * SO_RCVBUF        表示接收数据的缓冲区大小
 * SO_KEEPALIVE     表示对于长时间处于空闲状态的Sokcet，是否要自动把它关闭，默认值为false
 * OOBINGLINE       表示是否支持发送1字节的TCP紧急数据，默认值为false
 *
 *
 * IP服务类型选型
 * 设置服务类型：public void setTrafficClass(int trafficClass) throws SocketException
 * 获取服务类型：public int getTrafficClass() throws SocketException
 * 服务类型： 用1字节来表示，取值范围为0~255之间的整数，会被复制到TCP数据包头部的8位字段中
 *          高六位：表示DSCP值，表示不同的服务类型代码号，DSCP允许最多有64种服务类型
 *          低两位：表示ECN值，即显示拥塞通知信息
 *
 * 常见的DCSP值：
 *      默认服务类型：000000
 *      加速转发类型：101110        特点时低损耗、低延迟、低抖动
 *      保证转发类型：12个取值       保证以指定速率转发
 *
 *
 * 设置连接时间、延迟和带宽的相对重要性
 * public void setPerformancePreferences(int connectionTime, int latency, int bandwidth)
 * 参数connectionTime：表示用最少时间建立连接
 * 参数latency：表示最小延迟
 * 参数bandwidth：表示最高带宽
 * setPerformancePreferences(2, 1, 3);  表示最高带宽最重要，其次是最少连接时间，最后是最小延迟
 * 该方法的设置仅仅为底层的网络实现提供一个参考，有些底层Socket实现会忽略这一设置
 * */
public class SocketDemo2 {
    public void test() throws IOException {
        Socket socket = new Socket();
        if(!socket.getReuseAddress())
            socket.setReuseAddress(true);
            /*
             * socket.setReuseAddress(true);
             * 允许重用Socket所绑定的本地地址
             * 确保一个进程关闭Socket后，即使它还未释放端口，同主机上其他进程也可立刻重用该端口
             * 必须在Socket还未被绑定到一个本地端口之前调用，否则设置无效
             * 两个共用同一端口的进程必须都调用该方法才能使一个进程关闭Socket后另一个进程立即重用相同端口
             * */
        SocketAddress localAddress = new InetSocketAddress("127.0.0.1", 9003);
        SocketAddress remoteAddress = new InetSocketAddress("www.javathinker.net", 80);
        socket.bind(localAddress);      // 绑定本地端口
        socket.connect(remoteAddress);  // 连接远程服务器
        if(!socket.getTcpNoDelay())
            socket.setTcpNoDelay(true);     // 设置为立即发送数据
        if(socket.getSoTimeout() == 0)
            socket.setSoTimeout(6000*3);    // 设置等待超时时间为3分钟，默认为0，表示无限等待，不会超时
        socket.setSoLinger(true, 0);    // 设置为执行Socket的close()方法时立即关闭底层的Socket
        socket.setSoLinger(true, 60*10);    /*设置为执行Socket的close()方法时进入阻塞状态，
                                                        底层的Socket会尝试发送剩余的数据*/
        /*
        * socket.setSoLinger(true, 60*10);
        * 第二参数单位：秒
        * 执行socket.close()方法时满足以下两个条件之一，close()方法才会返回
        *       底层的Socket已经发送完所有的剩余数据
        *       底层的Socket未发完送所有的剩余数据，但已阻塞360秒，阻塞时间超时，close()方法，未发送的数据被丢弃
        * */

        int size = socket.getReceiveBufferSize();
        System.out.println(size);
        socket.setReceiveBufferSize(1024);
        size = socket.getSendBufferSize();
        System.out.println(size);
        socket.setSendBufferSize(1024);
        if(!socket.getKeepAlive())
            socket.setKeepAlive(true);
        if(!socket.getOOBInline())
            socket.setOOBInline(true);

        // 加速转发类型比其他服务类型有更高的优先级
        socket.setTrafficClass(0xb8);   // 设置为加速转发类型，0xb8对应的二进制数据“10111000”
    }
}
